#include "Common.h"
#include "GLGraphics.h"
#include "TgaLoader.h"

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>

namespace GenBen {

GLTexture::GLTexture(const string& filename)
{
	void* texture_data = NULL;
	loadTga(filename, width, height, &texture_data);
	ASSERT(texture_data);

	GL_ERR(glGenTextures(1, (GLuint*)&tex_id));
	GL_ERR(glBindTexture(GL_TEXTURE_2D, tex_id));
	GL_ERR(glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data));
	
	free(texture_data);

	// Nice retro point filtering
	GL_ERR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
    GL_ERR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
}

GLTexture::~GLTexture()
{
	GL_ERR(glDeleteTextures(1, (GLuint*)&tex_id));
}

GLGraphics::GLGraphics(uint width, uint height, uint scale, uint refresh_rate, bool fullscreen, bool lcd)
{
	ASSERT(width >= 320 && width <= 1600);
	ASSERT(height >= 240 && height <= 1200);
	ASSERT(scale >= 1 && scale <= 4);
	
	// To prevent unused parameter warning
	refresh_rate = fullscreen = lcd = 0;

	uint flags = SDL_OPENGL | SDL_DOUBLEBUF;
	if(fullscreen)
		flags |= SDL_FULLSCREEN;

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	if(SDL_SetVideoMode(width, height, 32, flags) == NULL)
		criticalError("Unable to set video mode");

    GL_ERR(glEnable(GL_TEXTURE_2D));
	GL_ERR(glShadeModel(GL_SMOOTH));
    GL_ERR(glClearDepth(1.0f));

	GL_ERR(glViewport(0, 0, width, height));

	GL_ERR(glEnable(GL_BLEND));
    GL_ERR(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
	
	GL_ERR(glMatrixMode(GL_MODELVIEW));
	GL_ERR(glLoadIdentity());
	GL_ERR(glMatrixMode(GL_PROJECTION));
	GL_ERR(glLoadIdentity());
	GL_ERR(gluOrtho2D(0.0, (double)width, (double)height, 0.0));

	GLRect dummy;
	rects.assign(MAX_QUADS, dummy);
	rect_count = 0;
}

GLGraphics::~GLGraphics()
{
	// HACK
}

Texture* GLGraphics::loadTexture(string filename)
{
	return (Texture*)new GLTexture(filename);
}

string GLGraphics::getDescription()
{
	return string("OpenGL 1.2");
}

void GLGraphics::clear(const GenBen::Color &color)
{
	GL_ERR(glClearColor(color.r, color.g, color.b, color.a));
	GL_ERR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}

void GLGraphics::present()
{
	std::sort(rects.begin(), rects.begin() + rect_count);

	uint active_tex = 0;
	bool drawing = false;
	float width = 0.0f, height = 0.0f;

	for(uint i = 0; i < rect_count; ++i)
	{
		GLRect rect = rects[i];

		if(active_tex != rects[i].tex->getTexId())
		{
			if(drawing)
			{
				GL_ERR(glEnd());
				drawing = false;
			}
			active_tex = rect.tex->getTexId();
			GL_ERR(glBindTexture(GL_TEXTURE_2D, active_tex));

			width = (float)rect.tex->getWidth();
			height = (float)rect.tex->getHeight();
		}

		if(!drawing)
		{
			glBegin(GL_QUADS);
			drawing = true;
		}

		glColor4f(rect.tint.r, rect.tint.g, rect.tint.b, rect.tint.a);

		// Top left
		glTexCoord2f((float)rect.source.min.x / width, 1.0f - (float)rect.source.max.y / height); 
		glVertex2f((float)rect.dest.min.x, (float)rect.dest.max.y);

		// Top right
		glTexCoord2f((float)rect.source.max.x / width, 1.0f -(float)rect.source.max.y / height);
		glVertex2f((float)rect.dest.max.x, (float)rect.dest.max.y);

		// Bottom right
		glTexCoord2f((float)rect.source.max.x / width, 1.0f -(float)rect.source.min.y / height);
		glVertex2f((float)rect.dest.max.x, (float)rect.dest.min.y);
		
		// Bottom left
		glTexCoord2f((float)rect.source.min.x / width, 1.0f -(float)rect.source.min.y / height);
		glVertex2f((float)rect.dest.min.x, (float)rect.dest.min.y);
	}

	if(drawing)
	{
		GL_ERR(glEnd());
	}

	rect_count = 0;
	SDL_GL_SwapBuffers();
}

void GLGraphics::draw(Texture* texture, int layer, const RectI& source, const VectorI& dest, const Color& tint)
{
	draw(texture, layer, source, RectI(dest.x, dest.y, 0, 0), tint);
}

void GLGraphics::draw(Texture* texture, int layer, const RectI& source, const RectI& dest, const Color& tint)
{
	
	bool full_source = source.max.x == 0 && source.max.y == 0;
	bool full_dest = dest.max.x == 0 && dest.max.y == 0;

	GLTexture* tex = static_cast<GLTexture*>(texture);
	
	GLRect new_rect;
	new_rect.tint = tint;
	new_rect.tex = tex;
	new_rect.layer = layer;
	new_rect.dest = dest;
	new_rect.source = source;
	
	if(full_source)
	{
		new_rect.source.max.x = texture->getWidth() - new_rect.source.min.x;		
		new_rect.source.max.y = texture->getHeight() - new_rect.source.min.y;
	}

	if(full_dest)
	{
		int real_width = new_rect.source.max.x - new_rect.source.min.x;
		int real_height = new_rect.source.max.y - new_rect.source.min.y;
		new_rect.dest.max.x = new_rect.dest.min.x + real_width;
		new_rect.dest.max.y = new_rect.dest.min.y + real_height;
	}

	rects[rect_count++] = new_rect;

	if(rect_count >= MAX_QUADS)
		criticalError("Too many rects!");
}

void GLGraphics::draw(Texture* texture, int layer, const RectI& source, const VectorI& dest)
{
	draw(texture, layer, source, RectI(dest.x, dest.y, 0, 0), Color(1.0f, 1.0f, 1.0f));
}

void GLGraphics::draw(Texture* texture, int layer, const RectI& source, const RectI& dest)
{
	draw(texture, layer, source, dest, Color(1.0f, 1.0f, 1.0f));
}

}
